package feng.process;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Element;

import feng.process.api.user.IUserSelect;
import feng.process.base.bean.ProcessBean;
import feng.process.base.bean.StepBean;
import feng.process.base.step.StartStep;
import feng.process.base.step.TaskStep;
import feng.process.cont.ProcessConst;
import feng.process.load.ProcessConfigLoader;
import feng.xml.XmlFile;

/**
 * 流程管理模块启动器
 * @author 冯昌硕
 * @create 2012-10-22 下午10:24:01
 */
public class Launcher {
    
    private static final Logger log = Logger.getLogger(Launcher.class);
    
    /**
     * 配置文件加载器，主要用于加载conf.properties配置文件
     */
    private ProcessConfigLoader cfgLoader;
    
    private ProcessObject processObj;
    
    public Launcher() {
        cfgLoader = new ProcessConfigLoader();
    }

    /**
     * 流程引擎启动。
     * @return 启动成功返回true,启动失败返回false
     * @create 2012-10-29 上午12:03:10
     */
    public boolean start() {
        log.debug("流程模块开始启动 ...");
        
        log.debug("conf.properties文件加载模块开始启动...");
        boolean cfgLoaderRs = cfgLoader.init();
        if(!cfgLoaderRs) {
            log.warn("conf.properties 加载不成功，流程模块无法启动。");
            return false;
        }
        log.debug("conf.properties文件加载模块执行完毕");
        
        // 取流程配置文件目录
        log.debug("流程配置文件加载模块开始启动...");
        String processDir = cfgLoader.getValue(ProcessConst.KEY_PROCESS_DIR);
        
        // 取流程配置文件
        List<String> templeteFiles = cfgLoader.getProcessTempleteLst();
        
        // 根据配置分别加载其相应的流程配置文件
        String processFile = null;
        for(String templeteFile : templeteFiles) {
            if(templeteFile == null || "".equals(templeteFile)) {
                continue;
            }
            
            processFile = processDir + File.separator + templeteFile;
            log.debug("开始解析流程配置文件：processFile=["+ processFile +"]");
            boolean parseRs = parseProcess(processFile);
            if(parseRs) {
                log.info("流程配置文件：processFile=["+ processFile +"]解析成功");
            } else {
                log.info("流程配置文件：processFile=["+ processFile +"]解析失败，不能启动" + templeteFile + "流程");
            }
        }
        log.debug("流程配置文件加载模块启动完毕...");
        
        // 取数据库配置类型
        boolean dbTypeRs = dbTypeLoad();
        if(!dbTypeRs) {
            return false;
        }
        
        // 加载流程人员选择器
        String userSelecter = cfgLoader.getValue(ProcessConst.KEY_WAIT_USER_SELECTER);
        log.debug("流程人员选择器 userSelecter=["+userSelecter+"]");
        if(userSelecter == null || "".equals(userSelecter)) {
            log.warn("流程人员选择器 加载不成功，请检查conf.properties文件" + ProcessConst.KEY_WAIT_USER_SELECTER + "配置项");
            return false;
        }
        
        boolean userSelecterLoader = userSelecterClassLoader(userSelecter);
        log.debug("流程模块启动 结束...");
        
        return userSelecterLoader;
    }
    
    /**
     * 功能：加载指定的流程配置文件，解析相应的XML内容
     * @param xmlFilePath 流程配置文件路径
     * @return
     * @create 2012-10-22 下午11:21:34
     */
    private boolean parseProcess(String xmlFilePath) {
        
        // 解析xml文件
        XmlFile xmlFile = new XmlFile(xmlFilePath);
        boolean parseRs = xmlFile.parseXmlFile();
        if(!parseRs) {
            // 解析xml文件不成功
            return false;
        }
        
        // 取xml文件里的内容
        // 1) 取根结点
        try {
            
            processObj = new ProcessObject();
            
            // 构造根结点对应的ProcessBean对象
            Element rootElement = xmlFile.getRootElement();
            Attribute nameAttr = rootElement.attribute("templete-name");
            String templeteName = nameAttr.getValue();
            
            Attribute disNameAttr = rootElement.attribute("display-name");
            String displayName = disNameAttr.getValue();
            
            ProcessBean processBean = new ProcessBean();
            processBean.setTempleteName(templeteName);
            processBean.setDisplayName(displayName);
            log.debug("processBean=["+ processBean +"]");
            processObj.setProcessBean(processBean);
            
            // 构造流程启动步骤对象
            // <start-setp id="start" display-name="生成工单" class-name="">
            //   <next-step id="bprk"></next-step>
            // </start-setp>
            Element startStepEle = rootElement.element("start-setp");
            Attribute startStepIdAttr = startStepEle.attribute("id");
            String startStepId = startStepIdAttr.getValue();
            
            Attribute startStepDisnameAttr = startStepEle.attribute("display-name");
            String startStepDisname = startStepDisnameAttr.getValue();
            
            Attribute startStepClanameAttr = startStepEle.attribute("class-name");
            String startStepClaname = startStepClanameAttr.getValue();
            
            StepBean startStepBean = new StepBean();
            startStepBean.setId(startStepId);
            startStepBean.setDisplayName(startStepDisname);
            startStepBean.setClassName(startStepClaname);
            
            List<String> nextSteps = getNextSteps(startStepEle);
            startStepBean.setNextStepIds(nextSteps);
            
            log.debug("startStepBean=["+ startStepBean +"]");
            
            // 这里构造启动流程步骤实现类的实例
            boolean startIns = getStartStepInstance(startStepClaname);
            if(!startIns) {
                log.info("get StartStepInstance failed, 流程["+templeteName+"]初始失败");
                return false;
            }
            
            processObj.setStartStepBean(startStepBean);
            
            // 取steps环节步骤子结点
            Element stepsEle = rootElement.element("steps");
            List<StepBean> stepBeanLst = new ArrayList<StepBean>();
            boolean parsedStep = parseSteps(stepBeanLst, stepsEle);
            // 环节步骤子结点实例化或解析失败
            if(!parsedStep) {
                log.info("环节步骤子结点实例化或解析失败, 流程["+templeteName+"]初始失败");
                return false;
            }
            
            processObj.setStepBeanLst(stepBeanLst);
            
            ProcessDataInfo.processMap.put(templeteName, processObj);
            
        } catch(Exception e) {
            log.error("XML内容解析操作异常：" + e.toString());
            e.printStackTrace();
            return false;
        }
        
        return true;
    }
    
    /**
     * 功能：获取流程启动步骤的操作实例
     * @param className 流程启动步骤类
     * @create 2012-10-23 上午12:26:57
     */
    private boolean getStartStepInstance(String className) {
        try {
            StartStep obj = (StartStep)Class.forName(className).newInstance();
            processObj.setStartStep(obj);
            return obj.init();
        } catch (ClassNotFoundException e) {
            log.error("未找到类["+ className +"],流程启动步骤初始化异常：" + e.toString());
            e.printStackTrace();
            return false;
        } catch (InstantiationException e) {
            log.error("流程启动步骤初始化异常 InstantiationException ： " + e.toString());
            e.printStackTrace();
            return false;
        } catch (IllegalAccessException e) {
            log.error("流程启动步骤初始化异常 IllegalAccessException ： " + e.toString());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 功能：从指定结点对象中获取其<next-step></next-step>子结点并进行解析
     * @param ele 要解析的结点，其中需要有next-step子结点
     * @return 解析后的结果对象
     * @create 2012-10-23 下午8:16:21
     */
    private List<String> getNextSteps(Element ele) {
        List<String> nextSteps = new ArrayList<String>();
        
        List<?> nextEleLst = ele.elements("next-step");
        for(int i = 0; i < nextEleLst.size(); i++) {
            Element nextStepEle = (Element) nextEleLst.get(i);
            Attribute nextStepIdAttr = nextStepEle.attribute("id");
            String nextStepId = nextStepIdAttr.getValue();
            nextSteps.add(nextStepId);
        }
        return nextSteps;
    }
    
    /**
     * 功能：解析<steps/>结点下的各子结点信息
     * @param ele steps结点对象
     * @return
     * @create 2012-10-23 下午8:18:42
     * <steps>
     *   <step id="bprk" display-name="白坯入库" class-name="">
     *     <next-step id="bpjgck"></next-step>
     *   </step>
     *   <step id="bpjgck" display-name="白坯出库加工" class-name="">
     *     <next-step id="cprk"></next-step>
     *   </step>
     * </steps>
     */
    private boolean parseSteps(List<StepBean> stepBeanLst, Element ele) {
        boolean parsedStep = true;
        
        List<?> stepEleLst = ele.elements("step");
        for(int i = 0; i < stepEleLst.size(); i++) {
            StepBean stepBean = new StepBean();
            
            Element stepEle = (Element) stepEleLst.get(i);
            Attribute stepIdAttr = stepEle.attribute("id");
            String stepId = stepIdAttr.getValue();
            stepBean.setId(stepId);
            
            Attribute stepDisNameAttr = stepEle.attribute("display-name");
            String stepDisName = stepDisNameAttr.getValue();
            stepBean.setDisplayName(stepDisName);
            
            Attribute stepClassNameAttr = stepEle.attribute("class-name");
            String stepClassName = stepClassNameAttr.getValue();
            stepBean.setClassName(stepClassName);
            
            Attribute stepUserSelRoleAttr = stepEle.attribute("user-select-role");
            String stepUserSelRole = stepUserSelRoleAttr.getValue();
            stepBean.setUserSelRole(stepUserSelRole);
            
            // 反射实例化环节步骤实现类
            boolean stepInstanced = getStepInstance(stepId, stepClassName);
            if(!stepInstanced) {
                parsedStep = false;
                break;
            }
            
            List<String> nextStepsLst = getNextSteps(stepEle);
            stepBean.setNextStepIds(nextStepsLst);
            
            stepBeanLst.add(stepBean);
        }
        
        return parsedStep;
    }
    
    /**
     * 功能：获取流程步骤实例
     * @param className 流程步骤实现类
     * @return
     * @create 2012-10-23 下午11:33:51
     */
    private boolean getStepInstance(String id, String className) {
        try {
            TaskStep obj = (TaskStep)Class.forName(className).newInstance();
            processObj.addTask(id, obj);
            return obj.init();
        } catch (ClassNotFoundException e) {
            log.error("未找到类["+ className +"],流程环节步骤初始化异常：" + e.toString());
            e.printStackTrace();
            return false;
        } catch (InstantiationException e) {
            log.error("流程环节步骤初始化异常 InstantiationException ： " + e.toString());
            e.printStackTrace();
            return false;
        } catch (IllegalAccessException e) {
            log.error("流程环节步骤初始化异常 IllegalAccessException ： " + e.toString());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 加载人员选择实现类
     * 
     * @param userSelecterClass 要加载的类
     * @return 加载成功返回true，加载失败返回false
     * @create 2012-10-29 上午12:08:15
     */
    private boolean userSelecterClassLoader(String userSelecterClass) {
        log.debug("开始加载人员选择类：userSelecterClass=["+userSelecterClass+"]");
        
        if((null == userSelecterClass) || ("".equals(userSelecterClass))) {
            log.warn("人员选择类名参数值为空，无法加载。");
            return false;
        }
        
        try {
            Object classIns = Class.forName(userSelecterClass).newInstance();
            
            if(classIns instanceof IUserSelect) {
                ProcessDataInfo.userSelecter = (IUserSelect) classIns;
            } else {
                log.warn("人员选择类配置值需要实现feng.process.api.user.IUserSelect接口");
                return false;
            }
            
        } catch (InstantiationException e) {
            log.error("userSelecterClass-["+userSelecterClass+"]反射InstantiationException异常：" + e.toString());
            e.printStackTrace();
            return false;
        } catch (IllegalAccessException e) {
            log.error("userSelecterClass-["+userSelecterClass+"]反射IllegalAccessException异常：" + e.toString());
            e.printStackTrace();
            return false;
        } catch (ClassNotFoundException e) {
            log.error("userSelecterClass-["+userSelecterClass+"]反射IllegalAccessException异常：" + e.toString());
            e.printStackTrace();
            return false;
        }
        
        return true;
    }
    
    /**
     * 获取引擎使用的数据库类型
     * @return
     * @create 2012-11-5 下午7:39:15
     */
    private boolean dbTypeLoad() {
        log.debug("开始获取数据库配置类型 ...");
        String dbType = cfgLoader.getValue("process.dbtype");
        if((null == dbType) || ("".equals(dbType.trim()))) {
            log.info("数据库类型为空，请检查配置文件conf.properties的process.dbtype配置项。");
            return false;
        }
        
        ProcessDataInfo.dbType = dbType.toUpperCase();
        
        log.debug("数据库配置类型为：["+ ProcessDataInfo.dbType +"]");
        return true;
    }
}
